#include <Stepper.h>
#include <ACI_10K_an.h>

#define CMD_NONE 0
// 无任何操作
#define CMD_STOP 1
// 妇洗
#define CMD_FRONT 2
// 洗便
#define CMD_REAR 3
// 服务
#define CMD_SERVE 4
// 烘干
#define CMD_DRY 5
// 移动
#define CMD_MOVE 6
// 冲水
#define CMD_FLUSH 7

// 清洗停止
#define CLEAN_STOP 0
// 已伸出喷头
#define CLEAN_NOZZLE_OPEN 1
// 正在喷水
#define CLEAN_SPRAYING 2
// 已收回喷头
#define CLEAN_NOZZLE_CLOSED 3

#ifndef DEBUG
#define DEBUG true
#endif
// 按键1停止 Stop
#define STOP_KEY_PIN 2
// 按键2妇洗 Front
#define FRONT_KEY_PIN 22
// 按键3洗便 Rear
#define REAR_KEY_PIN 24
// 按键4服务 Serve
#define SERVE_KEY_PIN 26
// 按键5烘干 Dry
#define DRY_KEY_PIN 28
// 按键6移动 Move
#define MOVE_KEY_PIN 30
// 按键7冲水 Flush
#define FLUSH_KEY_PIN 32
// LED 坐下 SEAT
#define SEAT_LED_PIN 34
// LED 电源 POWER
#define POWER_LED_PIN 36

// 进气风扇引脚
#define INLET_FAN_RELAY_PIN 38
// 吹风风扇引脚
#define BLOW_FAN_RELAY_PIN 40
// 臭氧发生器引脚
#define OZONE_GENERATOR_RELAY_PIN 42
// 供水电磁阀引脚
#define WATER_SOLENOID_VALVE_RELAY_PIN 44
// 水箱加热引脚
#define WATER_BOX_HEATER_RELAY_PIN 46
// 吹风加热引脚
#define BLOW_HEATER_RELAY_PIN 48
// 垫圈加热引脚
#define WASHER_HEATER_RELAY_PIN 50
// 打开喷头需要消耗的时间，单位秒
#define OPEN_NOZZLE_COST_TIME_SEC 5

// -------------------------定义设置变量-------------------------------
// 水箱温度
int settingWaterBoxTemp = 30;
// 吹风温度
int settingBlowTemp = 30;
// 垫圈温度
int settingWasherTemp = 25;
// 吹风模式 0:冷风 1：热风
int settingBlowMode = 1;
// 喷头最大长度
int settingNozzleMaxDistance;
// 喷头最小长度
int settingNozzleMinDistance;
// 喷头出水模式（0：关闭；1：1模式；2:2模式;3:3模式）
int settingNozzleEffluentMode;
// 喷头出水量（1-17，最大17档）
int settingNozzleRate;
// 喷头清洗时间，单位为秒
int settingCleanNozzleTimeSec = 5;
// 烘干运行周期数
int settingBlowRuningCountdown = 60 * 10;
// 洗便运行周期数
int settingNozzleRuningCountdown = 60 * 10;
// 臭氧生成运行周期数
int settingOzoneGenerateRuningCountdown = 600 * 10;
// ------------------------------------------------------------------
// 临时命令
int tempCmd = CMD_NONE;
// 命令
int cmd;
// 命令版本号
int cmdVersion;
// 吹风运行倒计时(单位为周期)
long runtimeBlowRuningCountdown;
// 喷头运行倒计时(单位为周期)
long runtimeNozzleRuningCountdown;
// 臭氧生成运行倒计时(单位为周期)
long runtimeOzoneGenerateRuningCountdown;
// 喷头当前距离(0-700)
int runtimeMoveNozzleDistance;
// 实时水箱加热器状态
int runtimeWaterBoxHeaterStatus;
// 实时水箱温度
float runtimeWaterBoxTemp = 0;
// 实时吹风温度
float runtimeBlowTemp = 0;
// 实时马桶垫圈温度
float runtimeWasherTemp = 0;
// 清洗状态
int runtimeCleanStatus = CLEAN_STOP;
// 烘干状态(0:停止；1:工作)
int runtimeDryStatus;
// 臭氧生成器状态(0:停止；1:工作)
int runtimeOzoneGenratorStatus = 0;

// 喷头移动步进电机
Stepper nozzleMoveStepperMotor(48, 4, 6, 5, 7); 
// 喷头出水阀门步进电机
Stepper nozzleEffluentSteppermotor(48, 8, 10, 9, 11); 
// 水箱温度NTC实例
Aci_10K waterBoxTempNTC;
// 吹风温度NTC实例
Aci_10K blowTempNTC;
// 垫圈温度NTC实例
Aci_10K washerTempNTC;
/**
* 初始化步进电机
*/
void initSteppingMotor(){
  // 喷头伸出步进电机
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  // 喷头阀门步进电机
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  // 冲水阀门步进电机
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
}
/**
* 初始化按键定义
*/
void initKeys(){
  // 按键1停止 Stop
  pinMode(STOP_KEY_PIN, INPUT_PULLUP);
  // 按键2妇洗 Front
  pinMode(FRONT_KEY_PIN, INPUT_PULLUP);
  // 按键3洗便 Rear
  pinMode(REAR_KEY_PIN, INPUT_PULLUP);
  // 按键4服务 Serve
  pinMode(SERVE_KEY_PIN, INPUT_PULLUP);
  // 按键5烘干 Dry
  pinMode(DRY_KEY_PIN, INPUT_PULLUP);
  // 按键6移动 Move
  pinMode(MOVE_KEY_PIN, INPUT_PULLUP);
  // 按键7冲水 Flush
  pinMode(FLUSH_KEY_PIN, INPUT_PULLUP);
  // LED 坐下 SEAT
  pinMode(SEAT_LED_PIN, OUTPUT);
 // LED 电源 POWER
  pinMode(POWER_LED_PIN, OUTPUT);
}

/**
* 初始化温度传感器
*/
void initTempSensor(){
  // 水箱温度 NTC10K
  pinMode(A0, INPUT);
  // 吹风温度 NTC10K
  pinMode(A1, INPUT);
  // 垫圈温度 NTC10K
  pinMode(A2, INPUT);
}
/**
* 初始化继电器
*/
void initRelay(){
  // 进气风扇
  pinMode(INLET_FAN_RELAY_PIN, OUTPUT);
  // 烘干风扇
  pinMode(BLOW_FAN_RELAY_PIN, OUTPUT);
  // 臭氧发生器
  pinMode(OZONE_GENERATOR_RELAY_PIN, OUTPUT);
  // 供水电磁阀
  pinMode(WATER_SOLENOID_VALVE_RELAY_PIN, OUTPUT);
  // 水箱加热继电器
  pinMode(WATER_BOX_HEATER_RELAY_PIN, OUTPUT);
  // 吹风加热继电器
  pinMode(BLOW_HEATER_RELAY_PIN, OUTPUT);
  // 垫圈加热继电器
  pinMode(WASHER_HEATER_RELAY_PIN, OUTPUT);
}

/**
* 初始化霍尔传感器
*/
void initHallSensor(){
    pinMode(36, INPUT);
}

void setup(void) {
  Serial.begin(115200);
  // 初始化控制脚
  // 初始化继电器控制脚
  initRelay();
  // 初始化步进电机控制脚
  initSteppingMotor();
  // 初始化温度传感器控制脚
  initTempSensor();
  // 初始化霍尔传感器
  initHallSensor();
  // 初始化按键控制脚
  initKeys();
  // 智能马桶初始化完成
  Serial.println("Smart Closetool Success！");

  digitalWrite(SEAT_LED_PIN, LOW);
  digitalWrite(POWER_LED_PIN, HIGH);

  //----------------用于开发测试----------------------
  settingNozzleMinDistance = 500;
  settingNozzleMaxDistance = 700;
  // 注册Stop按键，在中断
  attachInterrupt(0, stopCmd, FALLING);
}

void loop(void) {
  // 1.从服务器获取指令,或者从按键获取命令并存放到tempCmd变量中，返回版本号（当前的毫秒数）
  long newCmdVersion = getCmd();  
  // 2.如果新命令版本高于本地的指令版本，则更新指令，否则继续执行上一次的命令
  if (newCmdVersion > cmdVersion) {
    // 更新命令
    cmd = tempCmd;
  }
  // 读取温度传感器温度
  readTemperature();
  // 执行命令
  if (cmd == CMD_STOP) { // 当前命令为停止,则需要根据前一命令执行的情况，进行关闭
    // 如果喷头伸出状态，则关闭喷头
    if(runtimeCleanStatus > 0){
      offWaterSolenoidValve();
      closeNozzle();
    }
    // 如果风扇和吹风加热打开，则关闭
    if(runtimeDryStatus == 1){
      offDry();
      runtimeBlowRuningCountdown = 0;
      runtimeDryStatus = 0;
    }
    if(runtimeOzoneGenratorStatus == 1){
      offOzoneGenrator();
      runtimeOzoneGenerateRuningCountdown = 0;
      runtimeOzoneGenratorStatus = 0;
    }
    cmd = CMD_NONE;
    tempCmd = CMD_NONE;
  } else if (cmd == CMD_FRONT) {  // 如果当前命令为妇洗
    washingStool(0);
  } else if (cmd == CMD_REAR) {  // 如果当前命令为洗便
    washingStool(1);
  } else if (cmd == CMD_DRY) {  // 如果当前命令为烘干
    dry();
  } else if (cmd == CMD_SERVE) {
    ozoneGenerate();
  } else if (cmd == CMD_FLUSH) {
    flush();
  } else {
    // 水箱的水位计连通情况下才能加热水箱，防止干烧
    // 如果实际水温小于设置温度，则设置加热
    if (runtimeWaterBoxTemp < settingWaterBoxTemp) {  
      // 水箱未加热状态
      if (runtimeWaterBoxHeaterStatus == 0) {
        //TODO 设置水箱加热
        Serial.println("start heart water box");
        runtimeWaterBoxHeaterStatus = 1;
        // 打开水箱加热
        onWaterBoxHeater();
      }
    } else {
      if (runtimeWaterBoxHeaterStatus == 1) {  // 水箱加热状态
        Serial.println("end heart water box");
        runtimeWaterBoxHeaterStatus = 0;
         // 关闭水箱加热
        offWaterBoxHeater();
      }
    }
    // Serial.println(runtimeWaterBoxTemp);
    // TODO 关闭所有风扇和加热
  }
  delay(100);
}
/**
* 停止命令
*/
void stopCmd(){
  // 如果按下STOP按键则清除当前命令
  if(digitalRead(STOP_KEY_PIN) == LOW){
    // 如果stop按键按下，高电平则清空命令
    tempCmd = CMD_STOP;
    cmd = CMD_STOP;
    log("click stop key");
  }
}
/*
  获取命令
*/
long getCmd() {
  // TODO 将从服务器获取的命令解析存放到全局变量
  int newVersion;
  // 如果按下妇洗按键则进行洗便操作
  newVersion = scanKey(FRONT_KEY_PIN, CMD_FRONT);
  if(newVersion > cmdVersion){
    // 闪烁电源指示灯
    blinkPowerLed();
    log("click Front button");
    return newVersion;
  }

  // 如果按下洗便按键则进行洗便操作
  newVersion = scanKey(REAR_KEY_PIN, CMD_REAR);
  if(newVersion > cmdVersion){
    // 闪烁电源指示灯
    blinkPowerLed();
    log("click Rear button");
    stopActivty(tempCmd);
    return newVersion;
  }
 // 如果按下服务按键则进行烘干操作
  newVersion = scanKey(SERVE_KEY_PIN, CMD_SERVE);
  if(newVersion > cmdVersion){
    // 闪烁电源指示灯
    blinkPowerLed();
    log("click Serve button");
    stopActivty(tempCmd);
    return newVersion;
  }
  // 如果按下烘干按键则进行烘干操作
  newVersion = scanKey(DRY_KEY_PIN, CMD_DRY);
  if(newVersion > cmdVersion){
    // 闪烁电源指示灯
    blinkPowerLed();
    log("click Dry button");
    stopActivty(tempCmd);
    return newVersion;
  }
  // 如果按下移动按键则进行烘干操作
  newVersion = scanKey(MOVE_KEY_PIN, CMD_MOVE);
  if(newVersion > cmdVersion){
    // 闪烁电源指示灯
    blinkPowerLed();
    log("click Move button");
    stopActivty(tempCmd);
    return newVersion;
  }
  // 如果按下冲水按键则进行烘干操作
  newVersion = scanKey(FLUSH_KEY_PIN, CMD_FLUSH);
  if(newVersion > cmdVersion){
   // 闪烁电源指示灯
    blinkPowerLed();
    log("click Flush button");
    return newVersion;
  }
  return newVersion;
}
/**
* 扫描按键
*/
int scanKey(int key, int cmd){
  if(digitalRead(key) == LOW){
    delay(20);
    if(digitalRead(key) == LOW){
      // 如果stop按键按下，高电平则清空命令
      tempCmd = cmd;
      while(digitalRead(key) == LOW);
      return cmdVersion + 1;
    }
  }
  return cmdVersion;
}
/**
* 停止活动
*/
void stopActivty(int currentCmd){
  // 如果喷头伸出状态，则关闭喷头
  if(cmd != currentCmd && runtimeCleanStatus > 0){
    offWaterSolenoidValve();
    closeNozzle();
  }
  // 如果风扇和吹风加热打开，则关闭
  if(cmd != currentCmd && runtimeDryStatus == 1){
    offDry();
    runtimeBlowRuningCountdown = 0;
    runtimeDryStatus = 0;
  }
  if(cmd != currentCmd && runtimeOzoneGenratorStatus == 1){
    offOzoneGenrator();
    runtimeOzoneGenerateRuningCountdown = 0;
    runtimeOzoneGenratorStatus = 0;
  }
}
/**
* 电源指示灯闪烁
*/
void blinkPowerLed(){
    int value = digitalRead(POWER_LED_PIN);
    for(int i=0; i < 6; i++){
      digitalWrite(POWER_LED_PIN, i%2 == 0 ? LOW : HIGH);
      delay(250);
    }
    digitalWrite(POWER_LED_PIN, value);
}


/**
* 清洗
* @param type 清洗类型 0:妇洗；1：洗便
*/
void washingStool(int type) {
  // 如果喷头未伸出，则执行伸出
  if (runtimeCleanStatus == CLEAN_STOP) {  
    // 1.关闭水箱加热，防止水漏电
    offWaterBoxHeater();
    // 2.关闭垫圈加热，防止水漏电
    offWasherHeater();
    // 打开喷头
    openNozzle(type);
    // 初始化喷头倒计时
    runtimeNozzleRuningCountdown = settingNozzleRuningCountdown;
    return;
  }

  if (runtimeCleanStatus == CLEAN_NOZZLE_OPEN) {  // 如果喷头状态为伸出，则执行清洗
    // 打开供水电磁阀
    onWaterSolenoidValve();
  }
  // 如果还有计数值，则进行倒计时
  if (runtimeNozzleRuningCountdown > 0) {
    // 减少喷头计数次数
    runtimeNozzleRuningCountdown--;
    // TODO 移动碰头，设置步进马达移动距离
  }
  Serial.println(runtimeNozzleRuningCountdown);
  if (runtimeNozzleRuningCountdown <= 0) {
    // 关闭供水电磁阀
    offWaterSolenoidValve();
    // 关闭喷头
    closeNozzle();
    // 清除命令
    cmd = CMD_NONE;
    // 归零喷头倒计时
    runtimeNozzleRuningCountdown = 0;
  }
}
/*
* 烘干
*/
void dry() {
  if(runtimeDryStatus == 0){
    runtimeBlowRuningCountdown = settingBlowRuningCountdown;
    runtimeDryStatus = 1;
    // TODO 打开吹风继电器
    onDry();
    return;
  }
  if(runtimeDryStatus == 1){
    if (runtimeBlowRuningCountdown > 0) {
      if(runtimeBlowTemp > settingBlowTemp){// 吹风温度高于设定温度则关闭加热
        offBlowHeater();
      }else if(runtimeBlowTemp + 2 < settingBlowTemp){// 吹风温度低于设定温度2度则打开加热
        onBlowHeater();
      }
      // 减少吹风运行次数
      runtimeBlowRuningCountdown--;
    }
    Serial.println(runtimeBlowRuningCountdown);
    if (runtimeBlowRuningCountdown <= 0) {
      offDry();
      runtimeDryStatus = 0;
      // 清除命令
      cmd = CMD_NONE;
      // 归零烘干倒计时
      runtimeBlowRuningCountdown = 0;
    }
  }
}

void ozoneGenerate(){
  if(runtimeOzoneGenratorStatus == 0){
    runtimeOzoneGenerateRuningCountdown = settingOzoneGenerateRuningCountdown;
    runtimeOzoneGenratorStatus = 1;
    onOzoneGenrator();
    return;
  }

  if(runtimeOzoneGenratorStatus == 1){
    if(runtimeOzoneGenerateRuningCountdown > 0){
      // 减少臭氧生成器运行次数
      runtimeOzoneGenerateRuningCountdown--;
    }
    if(runtimeOzoneGenerateRuningCountdown <= 0){
      offOzoneGenrator();
      runtimeOzoneGenerateRuningCountdown = 0;
      runtimeOzoneGenratorStatus = 0;
      cmd = CMD_NONE;
      tempCmd = CMD_NONE;
    }
  }
}

/*
* 打开喷头
* @param type 清洗类型 0:妇洗；1：洗便
*/
void openNozzle(int type){
  // TODO 设置步进电机需要旋转的步数
  log("begin open Nozzle");
  // 伸出喷头前进行清洗喷头，确保没有细菌
  cleanNozzle(settingCleanNozzleTimeSec);
  // TODO 根据type，选择需要执行的步数
  int willBeStep =  -(settingNozzleMaxDistance - runtimeMoveNozzleDistance);
  nozzleMoveStepperMotor.setSpeed(500); 
  nozzleMoveStepperMotor.step(willBeStep); 
  runtimeMoveNozzleDistance = settingNozzleMaxDistance;
  // 打开喷头完成后设置清洗状态为喷头打开
  runtimeCleanStatus = CLEAN_NOZZLE_OPEN;
  // 需要延时等待喷头移动到指定位置为止
  delay(OPEN_NOZZLE_COST_TIME_SEC * 1000);
  log("end open Nozzle");
}

/*
* 关闭喷头
*/
void closeNozzle(){
  // TODO 设置步进电机需要旋转的步数
  log("begin close Nozzle");
  // 将要执行的步数
  int willBeStep =  700;
  nozzleMoveStepperMotor.setSpeed(500); 
  nozzleMoveStepperMotor.step(willBeStep); 
  // 碰头移动距离为0
  runtimeMoveNozzleDistance = 0;
  // 打开喷头完成后设置清洗状态为喷头已关闭
  runtimeCleanStatus = CLEAN_NOZZLE_CLOSED;
  // TODO 需要延时。等待喷头移动到指定为止
  delay(OPEN_NOZZLE_COST_TIME_SEC * 1000);
  // 清洗喷头
  cleanNozzle(settingCleanNozzleTimeSec);
  // 打开喷头完成后设置清洗状态为清洗停止
  runtimeCleanStatus = CLEAN_STOP;
  log("end close Nozzle");
}
/**
* 清洗喷头
*/
void cleanNozzle(int timeSec){
  int cleanTimeMs = timeSec * 1000;
  log("begin clean Nozzle");
  log(cleanTimeMs);
  // TODO 打开阀门，即打开步进电机角度
  nozzleEffluentSteppermotor.setSpeed(500); 
  nozzleEffluentSteppermotor.step(-1705); 
  // 打开供水电磁阀
  onWaterSolenoidValve();
  // 设置清洗时间
  delay(timeSec * 1000);
  // 关闭供水电磁阀
  offWaterSolenoidValve();
  // 关闭阀门
  nozzleEffluentSteppermotor.setSpeed(500); 
  nozzleEffluentSteppermotor.step(1750); 
  delay(5 * 1000);
  // TODO 关闭阀门，即关闭步进电机角度
  log("end clean Nozzle");
}
/**
* 打开水箱加热
*/
void onWaterBoxHeater(){
  if(digitalRead(WATER_BOX_HEATER_RELAY_PIN) == LOW){
    log("on water box heater");
    digitalWrite(WATER_BOX_HEATER_RELAY_PIN, HIGH);
  }
}

/**
* 关闭水箱加热
*/
void offWaterBoxHeater(){
  log("off water box heater");
  digitalWrite(WATER_BOX_HEATER_RELAY_PIN, LOW);
}

/**
* 打开垫圈加热
*/
void onWasherHeater(){
  if(digitalRead(WASHER_HEATER_RELAY_PIN) == LOW){
    log("on washer heater");
    digitalWrite(WASHER_HEATER_RELAY_PIN, HIGH);
  }
  
}

/**
* 关闭垫圈加热
*/
void offWasherHeater(){
  log("off washer heater");
  digitalWrite(WASHER_HEATER_RELAY_PIN, LOW);
}
/**
* 打开吹风加热
*/
void onBlowHeater(){
 // 如果吹风加热关闭状态，则打开加热
 if(digitalRead(BLOW_HEATER_RELAY_PIN) == LOW){
    log("on blow heater");
    digitalWrite(BLOW_HEATER_RELAY_PIN, HIGH);
  }
}

/**
* 关闭吹风加热
*/
void offBlowHeater(){
  Serial.print("runtimeBlowTemp:");
  Serial.print(runtimeBlowTemp);
  Serial.print(" settingBlowTemp:");
  Serial.println(settingBlowTemp);
  log("off blow heater");
  digitalWrite(BLOW_HEATER_RELAY_PIN, LOW);
}
/**
* 打开供水电磁阀
*/
void onWaterSolenoidValve(){
  // 如果供水电磁阀为低电平，则设置为高电平打开电磁阀供水
  if(digitalRead(WATER_SOLENOID_VALVE_RELAY_PIN) == LOW){
    log("on water solenoid valve");
    digitalWrite(WATER_SOLENOID_VALVE_RELAY_PIN, HIGH);
  }
}
/**
* 关闭供水电磁阀
*/
void offWaterSolenoidValve(){
  log("off water solenoid valve");
  digitalWrite(WATER_SOLENOID_VALVE_RELAY_PIN, LOW);
}

void onOzoneGenrator(){
  log("on ozone generator");
  digitalWrite(OZONE_GENERATOR_RELAY_PIN, HIGH);
}

void offOzoneGenrator(){
  log("off ozone generator");
  digitalWrite(OZONE_GENERATOR_RELAY_PIN, LOW);
}

void onDry(){
  log("on dry fan");
  // 打开吹风风扇
  digitalWrite(BLOW_FAN_RELAY_PIN, HIGH);
  if (settingBlowMode == 1) {  // 如果吹风模式为加热
    // 打开吹风加热
    onBlowHeater();
  }else{
    offBlowHeater();
  }

}

void offDry(){
  // 关闭吹风加热
  offBlowHeater();
  log("waiting blow heater cool 5s");
  // 延迟，关闭吹风风扇
  delay(5 * 1000);
  log("off dry fan");
  digitalWrite(BLOW_FAN_RELAY_PIN, LOW);
}

void flush(){
  log("flush");
  cmd = CMD_NONE;
  tempCmd = CMD_NONE;
}

/**
* 读取传感器温度
*/
void readTemperature(){
  // 读取水箱NTC10K温度传感器电阻值
  int WaterBoxR = analogRead(A0);
  // 读取吹风NTC10K温度传感器电阻值
  int blowTempR = analogRead(A1);
  // 读取垫圈NTC10K温度传感器电阻值
  int washerTempR = analogRead(A2);
  // 更新水箱实时温度
  runtimeWaterBoxTemp = waterBoxTempNTC.getTemp(WaterBoxR);
  // 更新吹风实时温度
  // runtimeBlowTemp = blowTempNTC.getTemp(blowTempR);
  // 更新垫圈实时温度
  runtimeWasherTemp = washerTempNTC.getTemp(washerTempR);
}

void log(String log){
  if(DEBUG){
    Serial.println(log);
  }
}